package com.xbrother.lucene.controller;

import com.xbrother.dto.LuceneContentDto;
import com.xbrother.lucene.store.DatabaseIndexStore;
import com.xbrother.lucene.store.DbDirectory;
import com.xbrother.vo.R;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

/**
 * @author huangchangjin
 * @date 2025/4/24 16:23
 */
@Slf4j
@RestController
@RequestMapping("/api/v1/test")
public class LuceneTestController {
    @Autowired
    private DatabaseIndexStore databaseIndexStore;

    private static Directory directory = null;


    /**
     * 单例：创建一个单例的Directory
     */
    private Directory singleInstanceDirectory() {
        // 创建自定义的 DatabaseDirectory
        if (directory != null) {
            return directory;
        } else {
            directory = new DbDirectory(databaseIndexStore);
        }
        return directory;
    }

    @RequestMapping("/")
    public R test() throws IOException {
        demo();
        return R.success();
    }

    /**
     * 案例:
     */
    public void demo() throws IOException {
        Directory directory = singleInstanceDirectory();
        //preCheck(directory);

        // 1.1 标准的分词器
        StandardAnalyzer standardAnalyzer = new StandardAnalyzer();
        // 1.2 中文分词器
        IKAnalyzer analyzer = new IKAnalyzer();
        LuceneContentDto dto = new LuceneContentDto("1", "hello world");


        // 创建索引
        createIndex(directory, analyzer, dto);

        //deleteIndex(directory, analyzer, dto);


    }

    /**
     * 执行索引查询
     *
     * @param directory 目录
     * @param analyzer  分词器
     * @param operation 消息处理逻辑
     * @throws IOException
     */
    private static void executeIndexOperation(Directory directory, Analyzer analyzer, Consumer<IndexWriter> operation) throws IOException {
        IndexWriterConfig config = new IndexWriterConfig(analyzer);
        try (IndexWriter writer = new IndexWriter(directory, config)) {
            operation.accept(writer);
            writer.commit();
        }
    }


    /**
     * 创建索引
     */
    private static void createIndex(Directory directory, Analyzer analyzer, LuceneContentDto dto) throws IOException {
        try {
            // 先查询
            List<Map<String, Object>> maps = queryIndex(directory, analyzer, dto.getId());
            log.info("maps = {}", maps);

            if (maps.size() > 0) {
                updateIndex(directory, analyzer, dto);
            } else {
                saveIndex(directory, analyzer, dto);
            }
        } catch (Exception e) {
            log.error("Exception {}", e.getMessage(), e);
        }
    }


    /**
     * 保存索引
     */
    private static void saveIndex(Directory directory, Analyzer analyzer, LuceneContentDto dto) throws IOException {
        executeIndexOperation(directory, analyzer, writer -> {
            Document doc = new Document();
            doc.add(new StringField("id", dto.getId(), Field.Store.YES));
            doc.add(new TextField("content", dto.getContent(), Field.Store.YES));

            try {
                writer.addDocument(doc);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });

    }

    /**
     * 删除索引
     */
    private static void deleteIndex(Directory directory, Analyzer analyzer, LuceneContentDto dto) throws IOException {
        executeIndexOperation(directory, analyzer, writer -> {
            try {
                writer.deleteDocuments(new Term("id", dto.getId()));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * 更新索引
     */
    private static void updateIndex(Directory directory, Analyzer analyzer, LuceneContentDto dto) throws IOException {
        executeIndexOperation(directory, analyzer, writer -> {
            Document doc = new Document();
            doc.add(new StringField("id", dto.getId(), Field.Store.YES));
            doc.add(new TextField("content", dto.getContent(), Field.Store.YES));

            try {
                writer.updateDocument(new Term("id", dto.getId()), doc);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });

    }

    /**
     * 查询索引
     */
    private static List<Map<String, Object>> queryIndex(Directory directory, Analyzer analyzer, String keyword) throws IOException {
        List<Map<String, Object>> r = new ArrayList<>();
        try (IndexReader reader = DirectoryReader.open(directory);) {
            IndexSearcher searcher = new IndexSearcher(reader);
            Query query = new QueryParser("id", analyzer).parse(keyword);
            TopDocs topDocs = searcher.search(query, 1);


            for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
                // 获取当前结果的docid, 这个docid相当于就是这个数据在索引中的主键
                int docId = scoreDoc.doc;
                // 再根据主键docid，通过搜索器从索引里把对应的Document取出来
                Document d = searcher.doc(docId);
                List<IndexableField> fields = d.getFields();
                System.out.print("\t" + scoreDoc.score);

                Map<String, Object> data = new HashMap<>();
                for (IndexableField f : fields) {
                    String fieldName = f.name();
                    String originContent = d.get(fieldName);
                    System.out.print("\t" + fieldName + ":" + originContent);

                    data.put(fieldName, originContent);
                }
                r.add(data);
                System.out.println();
            }
        } catch (Exception e) {
            log.error("ParseException {}", e.getMessage(), e);
        }
        return r;
    }

    /**
     * 提前校验
     */
    private static void preCheck(Directory directory) throws IOException {
        // 检查是否有索引文件
        if (DirectoryReader.indexExists(directory)) {
            // 删除旧的索引文件
            for (String file : directory.listAll()) {
                directory.deleteFile(file);
            }
        }

        if (directory.listAll().length != 0) {
            CheckIndex checkIndex = new CheckIndex(directory);
            CheckIndex.Status status = checkIndex.checkIndex();
            if (!status.clean) {
                // 索引存在问题，尝试修复
                checkIndex.exorciseIndex(status);
            }
        }
    }

}
